<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Matter.js 线条绘制（移动端+PC端兼容）</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            touch-action: none; /* 禁止移动端默认触摸行为（如缩放、滚动） */
        }

        body {
            width: 100%;
            height: 100vh;
            background-color: #000;
            overflow: hidden;
            position: relative;
        }

        .info {
            position: fixed;
            top: 10px;
            left: 10px;
            color: #fff;
            font-size: 14px;
            background: rgba(0, 0, 0, 0.7);
            padding: 8px 12px;
            border-radius: 4px;
            z-index: 100;
        }

        #previewCanvas {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            pointer-events: none;
            z-index: 10;
        }
    </style>
</head>
<body>
    <div class="info">PC：按住鼠标拖拽 | 移动端：按住屏幕滑动 | 绘制单条物理线条</div>
    <canvas id="previewCanvas"></canvas>
    <script src="./src/lib/matter.js"></script>
    <script>
        const screenWidth = window.innerWidth;
        const screenHeight = window.innerHeight;
        let render, engine;
        const { Engine, Render, Runner, Bodies, Composite, Mouse, MouseConstraint, World, Constraint, Body } = Matter;

        const init = () => {
            // 1. 初始化物理引擎和渲染器
            engine = Engine.create();
            render = Render.create({
                element: document.body,
                engine: engine,
                options: {
                    width: screenWidth,
                    height: screenHeight,
                    wireframes: false,
                    background: '#000',
                    constraint: {
                        render: {
                            lineWidth: 6,
                            strokeStyle: '#FFC107'
                        }
                    }
                }
            });
            const physicsCanvas = render.canvas;
            const previewCanvas = document.getElementById('previewCanvas');
            const previewCtx = previewCanvas.getContext('2d');

            // 设置画布尺寸（适配移动端）
            previewCanvas.width = screenWidth;
            previewCanvas.height = screenHeight;
            // 修复高清屏模糊问题
            const dpr = window.devicePixelRatio || 1;
            previewCanvas.style.width = `${screenWidth}px`;
            previewCanvas.style.height = `${screenHeight}px`;
            previewCanvas.width = screenWidth * dpr;
            previewCanvas.height = screenHeight * dpr;
            previewCtx.scale(dpr, dpr);

            // 2. 创建基础物理物体
            const boxSize = Math.min(screenWidth, screenHeight) * 0.1;
            const boxA = Bodies.rectangle(screenWidth * 0.1, screenHeight * 0.2, boxSize, boxSize, { render: { fillStyle: '#4CAF50' } });
            const boxB = Bodies.rectangle(screenWidth * 0.55, screenHeight * 0.1, boxSize, boxSize, {
                restitution: 0.8,
                friction: 0.1,
                density: 0.5,
                angle: Math.PI / 3,
                render: { fillStyle: '#2196F3' }
            });

            // 边界
            const ground = Bodies.rectangle(screenWidth / 2, screenHeight - 10, screenWidth, 20, { isStatic: true, render: { fillStyle: '#9E9E9E' } });
            const leftWall = Bodies.rectangle(0, screenHeight / 2, 20, screenHeight, { isStatic: true, render: { fillStyle: '#9E9E9E' } });
            const rightWall = Bodies.rectangle(screenWidth, screenHeight / 2, 20, screenHeight, { isStatic: true, render: { fillStyle: '#9E9E9E' } });

            // 带贴图物体
            const realWallSize = Math.min(screenWidth, screenHeight) * 0.1;
            const realWall = Bodies.rectangle(screenWidth * 0.4, screenHeight * 0.3, realWallSize, realWallSize, {
                isStatic: false,
                render: {
                    sprite: {
                        texture: 'https://threejs.org/examples/textures/brick_diffuse.jpg',
                        xScale: realWallSize / 911,
                        yScale: realWallSize / 911
                    }
                }
            });

            Composite.add(engine.world, [boxA, boxB, ground, leftWall, rightWall, realWall]);

            // 3. 鼠标/触摸控制（核心兼容逻辑）
            const mouse = Mouse.create(physicsCanvas);
            render.mouse = mouse;

            const mouseConstraint = MouseConstraint.create(engine, {
                mouse: mouse,
                constraint: {
                    stiffness: 0.2,
                    render: { visible: false }
                }
            });
            World.add(engine.world, mouseConstraint);

            // 绘制核心变量
            let isDrawing = false;
            let pathPoints = [];
            const minDistance = 8; // 移动端减小点密度，更流畅
            const curveSmoothness = 0.1;
            const lineStiffness = 0.9;
            const lineDamping = 0.1;

            // 4. 坐标转换工具（适配移动端触摸坐标）
            const getCanvasPoint = (clientX, clientY) => {
                const rect = physicsCanvas.getBoundingClientRect();
                // 转换为画布坐标系（处理缩放和偏移）
                return {
                    x: (clientX - rect.left) * (physicsCanvas.width / rect.width),
                    y: (clientY - rect.top) * (physicsCanvas.height / rect.height)
                };
            };

            // 5. 实时预览线条
            const drawPreviewPath = () => {
                if (pathPoints.length < 2) return;

                previewCtx.clearRect(0, 0, screenWidth, screenHeight);
                previewCtx.beginPath();
                previewCtx.moveTo(pathPoints[0].x, pathPoints[0].y);

                // 平滑线条
                for (let i = 1; i < pathPoints.length - 1; i++) {
                    const p0 = pathPoints[i - 1];
                    const p1 = pathPoints[i];
                    const p2 = pathPoints[i + 1];
                    const cp1x = p1.x - (p2.x - p0.x) * curveSmoothness;
                    const cp1y = p1.y - (p2.y - p0.y) * curveSmoothness;
                    const cp2x = p1.x + (p2.x - p0.x) * curveSmoothness;
                    const cp2y = p1.y + (p2.y - p0.y) * curveSmoothness;
                    previewCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y);
                }

                previewCtx.strokeStyle = '#FFC107';
                previewCtx.lineWidth = 6;
                previewCtx.lineCap = 'round';
                previewCtx.lineJoin = 'round';
                previewCtx.stroke();
            };

            // 6. 生成物理线条
            const createPhysicsLine = () => {
                if (pathPoints.length < 2) return;

                // 创建质点
                const particles = [];
                const particleRadius = 3;
                const particleDensity = 0.1;

                pathPoints.forEach((point) => {
                    const particle = Bodies.circle(
                        point.x,
                        point.y,
                        particleRadius,
                        {
                            density: particleDensity,
                            friction: 0.5,
                            render: { visible: false }
                        }
                    );
                    particles.push(particle);
                    World.add(engine.world, particle);
                });

                // 连接约束
                for (let i = 0; i < particles.length - 1; i++) {
                    const constraint = Constraint.create({
                        bodyA: particles[i],
                        bodyB: particles[i + 1],
                        stiffness: lineStiffness,
                        damping: lineDamping,
                        render: {
                            lineWidth: 6,
                            strokeStyle: '#FFC107'
                        }
                    });
                    World.add(engine.world, constraint);
                }

                pathPoints = [];
                previewCtx.clearRect(0, 0, screenWidth, screenHeight);
            };

            // 7. PC端鼠标事件
            physicsCanvas.addEventListener('mousedown', (e) => {
                if (e.button === 0) {
                    isDrawing = true;
                    const point = getCanvasPoint(e.clientX, e.clientY);
                    pathPoints.push(point);
                }
            });

            physicsCanvas.addEventListener('mousemove', (e) => {
                if (!isDrawing) return;
                const point = getCanvasPoint(e.clientX, e.clientY);
                const lastPoint = pathPoints[pathPoints.length - 1];
                const distance = Math.hypot(point.x - lastPoint.x, point.y - lastPoint.y);
                if (distance > minDistance) {
                    pathPoints.push(point);
                    drawPreviewPath();
                }
            });

            physicsCanvas.addEventListener('mouseup', () => {
                if (isDrawing) {
                    createPhysicsLine();
                    isDrawing = false;
                }
            });

            physicsCanvas.addEventListener('mouseleave', () => {
                if (isDrawing) {
                    createPhysicsLine();
                    isDrawing = false;
                }
            });

            // 8. 移动端触摸事件（核心修复）
            physicsCanvas.addEventListener('touchstart', (e) => {
                e.preventDefault(); // 阻止默认触摸行为（如滚动）
                if (e.touches.length === 1) { // 仅支持单指绘制
                    isDrawing = true;
                    const touch = e.touches[0];
                    const point = getCanvasPoint(touch.clientX, touch.clientY);
                    pathPoints.push(point);
                }
            });

            physicsCanvas.addEventListener('touchmove', (e) => {
                e.preventDefault();
                if (!isDrawing || e.touches.length !== 1) return;
                const touch = e.touches[0];
                const point = getCanvasPoint(touch.clientX, touch.clientY);
                const lastPoint = pathPoints[pathPoints.length - 1];
                const distance = Math.hypot(point.x - lastPoint.x, point.y - lastPoint.y);
                if (distance > minDistance) {
                    pathPoints.push(point);
                    drawPreviewPath();
                }
            });

            physicsCanvas.addEventListener('touchend', () => {
                if (isDrawing) {
                    createPhysicsLine();
                    isDrawing = false;
                }
            });

            physicsCanvas.addEventListener('touchcancel', () => {
                if (isDrawing) {
                    createPhysicsLine();
                    isDrawing = false;
                }
            });

            // 9. 运行引擎
            Render.run(render);
            const runner = Runner.create();
            Runner.run(runner, engine);

            // 10. 窗口自适应
            window.addEventListener('resize', () => {
                const newWidth = window.innerWidth;
                const newHeight = window.innerHeight;
                Render.setSize(render, newWidth, newHeight);
                previewCanvas.width = newWidth * dpr;
                previewCanvas.height = newHeight * dpr;
                previewCtx.scale(dpr, dpr);

                Body.setPosition(ground, { x: newWidth / 2, y: newHeight - 10 });
                Body.setSize(ground, { width: newWidth, height: 20 });
                Body.setPosition(rightWall, { x: newWidth, y: newHeight / 2 });
                Body.setSize(rightWall, { width: 20, height: newHeight });
            });
        };

        init();
    </script>
</body>
</html>